<?php
namespace plugins\module_config\controller;

use app\common\lib\file_operate\FileOperate as FO; //引入此类
use app\common\lib\file_operate\MakeZip;
use app\common\lib\user_operate\ModuleOperate as MO;
use app\common\lib\user_operate\PluginOperate;
use app\common\model\PluginDbTableModel;
use cmf\controller\PluginAdminBaseController;
use plugins\module_config\model\PluginApiIndexModel as Model;
use think\Config;
use think\Db;

/**
 * 安装检查控制器
 */
class InstallCheckController extends PluginAdminBaseController
{
    /**
     * 配置检查项
     */
    protected $checkItem = [
        0 => [
            'item' => 'moduleFileExist',
        ],
    ];
    /**
     * 获取检查项
     * @return [type] [description]
     */
    public function getCheckItem()
    {
        return $this->checkItem;
    }
    /**
     * 开启检查项
     * @param  integer $index [description]
     * @return [type]         [description]
     */
    public function check($index = 0)
    {
        $data   = $this->request->post();
        $param  = isset($data['param']) ? $data['param'] : [];
        $method = $this->checkItem[$index]['item'];
        return call_user_func_array([$this, $method], $param);
    }
    /**
     * 检查文件是否存在
     */
    public function moduleFileExist($plugin_name = '', $module_name = '', $id = '')
    {
        $data = $this->request->param();
        //模块文件检查
        self::returnStr('模块文件检查', '模块文件不存在', 201);
        self::returnStr('执行模块文件下载', '模块文件下载中，请稍后', 201, cmf_plugin_url('ModuleConfig://InstallCheck/download') . '?plugin_name=' . $plugin_name . '&module_name=' . $module_name . '&id=' . $id);
        return zy_json_echo(true, self::$returnStrArray);
    }
    /**
     * 文件下载
     */
    public function download($plugin_name = '', $module_name = '', $id = '')
    {
        //获取下载链接
        $po          = new PluginOperate();
        $url         = $po->getDownloadUrl($id)['data'];
        $pluginsPath = PLUGINS_PATH;
        //执行下载
        $fo  = new FO();
        $res = $fo->fileDownload($url, $pluginsPath);
        if (isset($res['status']) && $res['status'] == 'error') {
            self::returnStr('模块文件下载', $res['message'], 102);
            //安装失败卸载插件
            // $this->uninstall( $plugin_name , $module_name , true );
            return zy_json_echo(true, self::$returnStrArray);
        }
        self::returnStr('模块文件下载', '下载完成');
        self::returnStr('模块文件下载', '文件解压中', 201, cmf_plugin_url('ModuleConfig://InstallCheck/unZip') . '?plugin_name=' . $plugin_name . '&module_name=' . $module_name . '&file_name=' . $res['data']);
        return zy_json_echo(true, self::$returnStrArray);
    }
    /**
     * 执行文件解压
     */
    public function unZip($plugin_name = '', $module_name = '', $file_name = '')
    {
        $mz      = new MakeZip();
        $outPath = PLUGINS_PATH;
        self::returnStr('模块文件解压', $outPath);
        $res = $mz->unZip($file_name, $outPath);
        if ($res['status'] == 'error') {
            self::returnStr('模块文件解压', $res['message'], 101);
            return zy_json_echo(true, self::$returnStrArray);
        }
        self::returnStr('模块文件解压', $res['message']);
        // self:returnStr('安装插件','插件安装中',201,cmf_plugin_url('ModuleConfig://InstallCheck/install').'?name='.$name)
        self::returnStr('模块文件移动', '文件移动中', 201, cmf_plugin_url('ModuleConfig://InstallCheck/moveFile') . '?plugin_name=' . $plugin_name . '&module_name=' . $module_name);
        return zy_json_echo(true, self::$returnStrArray);
    }
    /**
     * 获取文件目录
     */
    public function getFileDir($path, &$relationArr)
    {
        if (!is_dir($path) || !is_array($relationArr)) {
            return false;
        }
        if ($dh = opendir($path)) {
            while ($file = readdir($dh)) {
                if (in_array($file, array('.', '..', null))) {
                    continue;
                }
                //无效文件，重来
                if (is_dir($path . DS . $file)) {
                    $relationArr[$file] = array(
                        'file_name' => $file,
                        'is_dir'    => true,
                        'children'  => array(),
                    );
                    $this->getFileDir($path . DS . $file, $relationArr[$file]['children']);
                } else {
                    $extension          = strchr($file, '.');
                    $relationArr[$file] = array(
                        'file_name' => $file,
                        'is_dir'    => false,
                        'children'  => array(),
                    );
                }
            }
        }
    }
    /**
     * 记录安装日志
     */
    public function record($plugin_name, $module_name, $data, $tags)
    {
        $plugin_name = cmf_parse_name($plugin_name);
        $rec         = getModuleConfig($module_name, 'config/install', $plugin_name . '.log');
        $rec         = json_decode($rec, true);
        $rec[$tags]  = $data;
        saveModuleConfigData($module_name, 'config/install', $plugin_name . '.log', $rec);
    }
    /**
     * 移动文件
     */
    public function moveFile($plugin_name = '', $module_name = '')
    {
        $fo = new FO();
        //插件目录
        $dir_path = ROOT_PATH . 'public/plugins/' . cmf_parse_name($plugin_name);
        //模块目录
        $module_path = ROOT_PATH . 'public/plugins/' . cmf_parse_name($module_name);
        $relationArr = array(
            $dir_path => array(
                'file_name' => $dir_path,
                'is_dir'    => true,
                'children'  => array(),
            ),
        );
        //获取文件目录
        $this->getFileDir($dir_path, $relationArr[$dir_path]['children']);
        $child = $relationArr[$dir_path]['children'];
        //获取插件最外层文件名
        $ks = array_keys($child);
        //安装日志
        $record = [];
        foreach ($ks as $k1 => $v1) {
            //插件目录
            $d = $dir_path . '/' . $v1;
            //模块目录
            $m = $module_path . '/' . $v1;
            $e = $child[$v1]; //文件
            //存入记录不带ROOT_PATH
            $cunr = 'public/plugins/' . cmf_parse_name($module_name) . '/' . $v1;
            //config 文件移动tag
            if ($v1 == 'config') {
                foreach ($e['children'] as $key_config => $val_config) {
                    if ($key_config == 'tag') {
                        foreach ($e['children']['tag']['children'] as $tag_k => $tag_k) {
                            $tgs = $d . '/tag/' . $tag_k;
                            $mgs = $m . '/tag/' . $tag_k;
                            //添加记录
                            $record[] = $cunr . '/tag/' . $tag_k; //$mgs;
                            $fo->moveFile($tgs, $mgs);
                        }
                    } else {
                        $this->movePublic($d, $m, $val_config, $record, $cunr, $fo);
                    }
                }
                continue;
            }
            if ($v1 == 'view') {
                if ($e['is_dir'] == true) {
                    foreach (array_keys($e['children']) as $k2 => $v2) {
                        if ($v2 == 'public') {
                            $this->movePublic($d, $m, $e['children']['public'], $record, $cunr, $fo);
                            continue;
                        }
                        $ds       = $d . '/' . $v2;
                        $ms       = $m . '/' . $v2;
                        $record[] = $cunr . '/' . $v2; //$ms;
                        if (is_dir($ds)) {
                            $fo->recurse_copy($ds, $ms);
                        } else {
                            $fo->moveFile($ds, $ms);
                        }
                    }
                }
                continue;
            }
            //如果是目录，则移动目录
            if ($e['is_dir'] == true) {
                foreach (array_keys($e['children']) as $k2 => $v2) {
                    $ds       = $d . '/' . $v2;
                    $ms       = $m . '/' . $v2;
                    $record[] = $cunr . '/' . $v2; //$ms;
                    if (is_dir($ds)) {
                        $fo->recurse_copy($ds, $ms);
                    } else {
                        $fo->moveFile($ds, $ms);
                    }
                }
            }
        }
        //记录安装
        $this->record($plugin_name, $module_name, $record, 'file_name');
        self::returnStr('模块文件移动', '移动成功');
        self::returnStr('执行插件安装', '安装中', 201, cmf_plugin_url('ModuleConfig://InstallCheck/install') . '?plugin_name=' . $plugin_name . '&module_name=' . $module_name);
        return zy_json_echo(true, self::$returnStrArray);
    }
    /**
     * 移动所有文件
     */
    /**
     * 移动公共文件
     */
    private function movePublic($plugin_path, $module_path, $dir = [], &$record, $cunr, &$fo)
    {
        if ($dir['is_dir'] == true) {
            $plugin_path .= '/' . $dir['file_name'];
            $module_path .= '/' . $dir['file_name'];
            $cunr .= '/' . $dir['file_name'];
            foreach ($dir['children'] as $key => $value) {
                $this->movePublic($plugin_path, $module_path, $value, $record, $cunr, $fo);
            }
        } else {
            $p_p      = $plugin_path . '/' . $dir['file_name'];
            $m_p      = $module_path . '/' . $dir['file_name'];
            $record[] = $cunr . '/' . $dir['file_name'];
            $fo->moveFile($p_p, $m_p);
        }
        return 1;
    }
    /**
     * 执行安装
     */
    public function install($plugin_name = '', $module_name = '')
    {
        //移动菜单
        $this->moveMenu($plugin_name, $module_name);
        //移动引入文件配置
        $this->moveHtml($plugin_name, $module_name);
        //添加表字段
        $this->addTableFields($plugin_name, $module_name);
        //添加插件表
        $this->addDbTable($plugin_name, $module_name);
        //删除插件目录
        $fo       = new FO();
        $dir_path = ROOT_PATH . 'public/plugins/' . cmf_parse_name($plugin_name);
        try {
            $fo->deldir($dir_path); //删除旧目录
        } catch (\Exception $e) {
            self::returnStr('执行安装', '安装失败，插件目录不存在,请检查压缩包是否正确', 202);
            //安装失败卸载插件
            $this->uninstall($plugin_name, $module_name, true);
            return zy_json_echo(true, self::$returnStrArray);
        }
        self::returnStr('执行安装', '安装成功');
        return zy_json_echo(true, self::$returnStrArray);
    }
    /**
     * 移动引入html配置
     */
    public function moveHtml($plugin_name, $module_name)
    {
        //1.读出配置文件内容
        //2.读出之后写入html.json 文件中
        //记录
        $record   = [];
        $p_config = getModuleConfig($plugin_name, 'config', cmf_parse_name($plugin_name) . '.json');
        $p_config = json_decode($p_config, true);
        if (empty($p_config['html'])) {
            return true;
        }
        $m_config = getModuleConfig($module_name, 'config', 'html.json');
        $m_config = json_decode($m_config, true);
        if (empty($m_config)) {
            $m_config['html'] = $p_config['html'];
        } else {
            $m_config['html'] = array_merge($m_config['html'], $p_config['html']);
        }
        $record = $m_config;
        //记录安装
        $this->record($plugin_name, $module_name, $record, 'html');
        saveModuleConfigData($module_name, 'config', 'html.json', $m_config);
    }
    /**
     * 安装表
     */
    public function addDbTable($plugin_name, $module_name)
    {
        $dbTable = new PluginDbTableModel();
        $res     = $dbTable->installDataTable(cmf_parse_name($plugin_name));
        //获取安装表名
        $sqlFileDirectory = PLUGINS_PATH . cmf_parse_name($plugin_name) . '/data';
        if (!file_exists($sqlFileDirectory)) {
            return false;
        }
        $directorys = scandir($sqlFileDirectory);
        $directorys = array_diff($directorys, ['.', '..']);
        $directorys = array_merge($directorys);
        
        foreach ($directorys as $key => $value) {
            $directorys[$key] = str_replace(strrchr($value, "."), "", $value);
        }
        $record = $directorys;
        //记录安装
        $this->record($plugin_name, $module_name, $record, 'table');
        if ($res) {
            return true;
        }
    }
    /**
     * 移动菜单
     */
    public function moveMenu($plugin_name, $module_name)
    {
        //记录
        $record   = [];
        $p_config = getModuleConfig($plugin_name, 'config', cmf_parse_name($plugin_name) . '.json');
        $p_config = json_decode($p_config, true);
        if (empty($p_config['nav'])) {
            return true;
        }
        $m_config = getModuleConfig($module_name, 'config', 'nav.json');
        $m_config = json_decode($m_config, true);
        if (empty($m_config)) {
            $m_config['nav'] = $p_config['nav'];
        } else {
            $m_config['nav'] = array_merge($m_config['nav'], $p_config['nav']);
        }
        $record = $m_config;
        //记录安装
        $this->record($plugin_name, $module_name, $record, 'nav');
        saveModuleConfigData($module_name, 'config', 'nav.json', $m_config);
    }
    /**
     * 添加表字段
     */
    public function addTableFields($plugin_name, $module_name)
    {
        $record   = [];
        $mo       = new MO();
        $p_config = getModuleConfig($plugin_name, 'config', cmf_parse_name($plugin_name) . '.json');
        $p_config = json_decode($p_config, true);
        if (empty($p_config['sql'])) {
            return true;
        }
        $record   = $p_config['sql'];
        $database = Config::get('database.database');
        foreach ($p_config['sql'] as $key => $value) {
            $add = $key . " "; //字段名称
            $add .= $value['type'] . '(' . $value['length'] . ') '; //类型
            $default = !empty($value['default']) ? $value['default'] : 'NULL';
            $add .= "DEFAULT " . $default . " "; //默认值
            $add .= "COMMENT '" . $value['desc'] . "'"; //字段注释
            if (!empty($value['table_name'])) {
                $table_name = $value['table_name'];
            } else {
                $model      = new Model();
                $table_name = $model->getTableName($module_name);
            }
            //判断字段是否存在
            $is_in = Db::query("SELECT column_name FROM information_schema.columns WHERE table_name = '{$table_name}' AND table_schema = '{$database}' AND column_name='{$key}'");
            if (!empty($is_in)) {
                //存在 修改
                Db::execute("ALTER TABLE `$table_name` MODIFY $add");
            } else {
                //不存在 添加
                try {
                    Db::execute("ALTER TABLE `$table_name` ADD $add");
                } catch (\Exception $f) {
                    self::returnStr('执行安装', $table_name . '表的' . $key . '字段添加失败:' . $f->getMessage(), 202);
                    //安装失败卸载插件
                    $this->uninstall($plugin_name, $module_name, true);
                    return zy_json_echo(true, self::$returnStrArray);
                }
            }
        }
        //记录安装
        $this->record($plugin_name, $module_name, $record, 'sql');
    }
    /**
     * 检查文件是否存在
     */
    private function checkModuleFileIsExist($symbol, $file = '')
    {
        $fileName = PLUGINS_PATH . DS . cmf_parse_name($symbol);
        if (!empty($file)) {
            $fileName = $fileName . DS . $file;
        }
        if (!file_exists($fileName)) {
            return false;
        }
        return $fileName;
    }
    /**
     * 状态返回
     * @return [type] [description]
     */
    protected static $returnStrArray = [];
    private static function returnStr($item = '', $msg = '', $code = 200, $operate = 'empty')
    {
        $data = [
            'item'    => $item,
            'msg'     => $msg,
            'code'    => $code,
            'operate' => $operate,
        ];
        self::$returnStrArray[] = $data;
    }
    /**
     * 卸载插件
     */
    public function uninstall($plugin_name, $module_name, $sysbom = false)
    {
        $data        = $this->request->param();
        $plugin_name = !empty($plugin_name) ? $plugin_name : $data['plugin_name'];
        $module_name = !empty($module_name) ? $module_name : $data['module_name'];
        //读安装日志文件
        $config = getModuleConfig($module_name, 'config/install', cmf_parse_name($plugin_name) . '.log');
        $config = json_decode($config, true);
        $tags   = array_keys($config);
        foreach ($tags as $k => $v) {
            switch ($v) {
                case 'file_name':
                    $this->unfileName($module_name, $config['file_name']);
                    break;
                case 'nav':
                    $this->unnav($module_name, $plugin_name);
                    break;
                case 'sql':
                    $this->unsql($module_name, $plugin_name, $config['sql']);
                    break;
                case 'table':
                    $this->unDbTable($module_name, $plugin_name, $config['table']);
                    break;
                case 'html':
                    $this->unhtml($module_name, $plugin_name, $config['html']);
                    break;
                default:
                    # code...
                    break;
            }
        }
        $record_file = ROOT_PATH . 'public/plugins/' . cmf_parse_name($module_name) . '/config/install/' . cmf_parse_name($plugin_name) . '.log';
        unlink($record_file);
        if ($sysbom == true) {
            //卸载目录
            $record_file = ROOT_PATH . 'public/plugins/' . cmf_parse_name($plugin_name);
            if (file_exists($record_file)) {
                $fo = new FO();
                $fo->deldir($record_file); //删除旧目录
            }
            return ['status' => true, 'message' => '卸载成功', 'code' => 200];
        }
        return $this->success('卸载成功');
    }
    /**
     * 删除文件
     */
    private function unfileName($module_name, $config)
    {
        if (empty($config)) {
            return true;
        }
        $fo = new FO();
        foreach ($config as $key => $value) {
            $v = ROOT_PATH . $value;
            //判断文件目录是否存在
            if (!file_exists($v)) {
                continue;
            }
            if (is_dir($v)) {
                $fo->deldir($v); //删除旧目录
            } else {
                unlink($v);
            }
        }
    }
    /**
     * 删除菜单
     */
    public function unnav($module_name, $plugin_name)
    {
        $config    = getModuleConfig($module_name, 'config', 'nav.json');
        $config    = json_decode($config, true);
        $da['nav'] = [];
        foreach ($config['nav'] as $key => $value) {
            if ($value['plugin'] == cmf_parse_name($plugin_name)) {
                //需要卸载的
                continue;
            }
            $da['nav'][] = $value;
        }
        saveModuleConfigData($module_name, 'config', 'nav.json', $da);
    }
    /**
     * 删除html.json
     */
    public function unhtml($module_name, $plugin_name)
    {
        $config     = getModuleConfig($module_name, 'config', 'html.json');
        $config     = json_decode($config, true);
        $da['html'] = [];
        foreach ($config['html'] as $key => $value) {
            if ($value['plugin'] == cmf_parse_name($plugin_name)) {
                //需要卸载的
                continue;
            }
            $da['html'][] = $value;
        }
        saveModuleConfigData($module_name, 'config', 'html.json', $da);
    }
    /**
     * 删除字段
     */
    public function unsql($module_name, $plugin_name, $config)
    {
        if (empty($config)) {
            return true;
        }
        foreach ($config as $key => $value) {
            try {
                if (!empty($value['table_name'])) {
                    $table_name = $value['table_name'];
                } else {
                    $model      = new Model();
                    $table_name = $model->getTableName($module_name);
                }
                Db::execute("ALTER TABLE `$table_name` drop column `$key`;");
            } catch (\Exception $e) {
                continue;
            }
        }
    }
    /**
     * 删除插件表
     */
    public function unDbTable($module_name, $plugin_name, $config)
    {
        $dropTableSql = " DROP TABLE IF EXISTS ";
        foreach ($config as $key => $value) {
            $dropTableSqls = $dropTableSql . $value;
            Db::execute($dropTableSqls);
        }
    }
}
